package org.jeecg.modules.easyExcel;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;

import java.util.*;

/**
 * 根据最长的内容设置列宽
 * 根据EasyExcel同名的类源码改变
 * 新增：可以设置额外增加的宽度以及是否需要跳过某些行
 * 新增：对日期类型单元格的支持
 * 新增：可以设置单元格最大长度
 * 修改：单元格长度计算按照字符数量，而非字节数量
 */
public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

    private final Map<String, Map<Integer, Integer>> cache = MapUtils.newHashMapWithExpectedSize(8);
    private final int totalMaxColumnWidth;
    private final int extraWidth;
    private final int[] rowIndexesToSkip;

    private CellStyle cellStyle;

    public LongestMatchColumnWidthStyleStrategy(int totalMaxColumnWidth, int extraWidth, int... rowIndexesToSkip) {
        this.totalMaxColumnWidth = totalMaxColumnWidth - extraWidth;
        this.extraWidth = extraWidth;
        this.rowIndexesToSkip = rowIndexesToSkip;
    }

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        // 计算考虑表头行数的真实行数 by 宿霄男
        int realRowIndex = isHead ? relativeRowIndex : head.getHeadNameList().size() + relativeRowIndex;
        if (needSetWidth)
            needSetWidth = !ArrayUtils.isEmpty(rowIndexesToSkip) && !ArrayUtils.contains(rowIndexesToSkip, realRowIndex);
        if (!needSetWidth) {
            return;
        }
        Map<Integer, Integer> maxColumnWidthMap = cache.computeIfAbsent(writeSheetHolder.getSheetName(), key -> new HashMap<>(16));
        Integer columnWidth = dataLength(cellDataList, cell, isHead);
        if (columnWidth < 0) {
            return;
        }
        if (columnWidth > totalMaxColumnWidth) {
            // 超过最大长度则设置自动换行
            setWrapText(cell);
            columnWidth = totalMaxColumnWidth;
        }
        Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
        if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
            maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth + extraWidth);
        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().length() * 256 * 3;
        }
        WriteCellData<?> cellData = cellDataList.get(0);
        CellDataTypeEnum type = cellData.getType();
        if (type == null) {
            return -1;
        }
        int notHeadlength;
        switch (type) {
            case STRING:
                String stringValue = cellData.getStringValue();
                String[] split = stringValue.split("\n");
                // 如果有换行符，则设置单元格自动换行
                if (split.length>1)
                    setWrapText(cell);
                notHeadlength = Arrays.stream(split).max(Comparator.comparingInt(String::length)).orElse("").length();
                break;
            case BOOLEAN:
                notHeadlength = cellData.getBooleanValue().toString().length();
                break;
            case NUMBER:
                notHeadlength = cellData.getNumberValue().toString().length();
                break;
            case DATE:
                notHeadlength = cellData.getDateValue().toString().length();
                break;
            default:
                notHeadlength = -1;
        }
        notHeadlength *= 180*3;
        return notHeadlength;
    }

    private void setWrapText(Cell cell) {
        if (cellStyle == null)
            cellStyle = cell.getSheet().getWorkbook().createCellStyle();
        cellStyle.setWrapText(true);
        cell.setCellStyle(cellStyle);
    }
}
